package com.google.code.jholidays.io.xml;

import java.io.File;
import java.util.List;

import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import com.google.code.jholidays.core.DescriptorCollection;
import com.google.code.jholidays.core.EventDescriptor;
import com.google.code.jholidays.io.IDescriptorReader;

/**
 * XML based implementation of {@link IDescriptorReader}.
 * <p>
 * Reads {@link DescriptorCollection} from XML file using SAX parser. XML
 * document must be well-formed and fit this schema:
 * <p>
 * 
 * <code><pre>
 * &ltxml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
 * &ltxml schema xmlns=&quot;http://www.w3.org/2001/XMLSchema&quot; targetNamespace=&quot;http://www.example.org/DescriptorCollectionXml&quot; xmlns:tns=&quot;http://www.example.org/DescriptorCollectionXml&quot; elementFormDefault=&quot;qualified&quot;&gt;
 * 
 *    &ltcomplexType name=&quot;EventDescriptor&quot;&gt;
 *    	&ltsequence&gt;
 *    		&ltelement name=&quot;id&quot; type=&quot;int&quot; maxOccurs=&quot;1&quot; minOccurs=&quot;1&quot;&gt;&lt;/element&gt;
 *    		&ltelement name=&quot;name&quot; type=&quot;string&quot; maxOccurs=&quot;1&quot;
 *    			minOccurs=&quot;1&quot;&gt;
 *    		&lt/element&gt;
 *    		&ltelement name=&quot;description&quot; type=&quot;string&quot; maxOccurs=&quot;1&quot;
 *    			minOccurs=&quot;0&quot;&gt;
 *    		&lt/element&gt;
 *    		&ltelement name=&quot;parent_id&quot; type=&quot;int&quot; maxOccurs=&quot;1&quot;
 *    			minOccurs=&quot;0&quot;&gt;
 *    		&lt/element&gt;
 *    		&ltelement name=&quot;expression&quot; type=&quot;string&quot; maxOccurs=&quot;1&quot; minOccurs=&quot;1&quot;&gt;&lt;/element&gt;
 *    	&lt/sequence&gt;
 *    &lt/complexType&gt;
 * 
 * 
 *    &ltcomplexType name=&quot;DescriptorCollection&quot;&gt;
 *    	&ltsequence&gt;
 *    		&ltelement name=&quot;EventDescriptor&quot; type=&quot;tns:EventDescriptor&quot; maxOccurs=&quot;unbounded&quot; minOccurs=&quot;0&quot;&gt;&lt;/element&gt;
 *    	&lt/sequence&gt;
 *    &lt/complexType&gt;
 * 
 *    &ltelement name=&quot;descriptors&quot; type=&quot;tns:DescriptorCollection&quot;&gt;&lt;/element&gt;
 * &lt/schema&gt;
 * </pre></code>
 * 
 * Here is <b>example</b> xml file which contains three event descriptors and
 * matches given schema: <code><pre>
 * &ltdescriptors xmlns="http://www.example.org/DescriptorCollectionXml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/DescriptorCollectionXml">
	&ltdescriptor>
		&ltid>1&lt/id>
		&ltname>first&lt/name>
		&ltdescription>none&lt/description>
		&ltexpression>10.01&lt/expression>
	&lt/descriptor>
	&ltdescriptor>
		&ltid>2&lt/id>
		&ltname>second&lt/name>
		&ltdescription>none&lt/description>
		&ltexpression>30.09&lt/expression>
	&lt/descriptor>
	&ltdescriptor>
		&ltid>3&lt/id>
		&ltname>third&lt/name>
		&ltdescription>none&lt/description>
		&ltparent_id>2&lt/parent_id>
		&ltexpression>+3&lt/expression>
	&lt/descriptor>
 * &lt/descriptors>
 * </pre></code> This xml contains 3 event descriptors. Special attention should
 * be paid to namespace declaration inside source xml file.
 * 
 * 
 * @author tillias
 * @see SaxParser
 * @see IDescriptorReader
 * @see XmlValidator
 * @see EventDescriptor
 * 
 */
public class XmlReader implements IDescriptorReader, IParserDirector {

    public XmlReader(String fileName) {
	this.fileName = fileName;
	parser = new SaxParser(this);
    }

    /**
     * {@inheritDoc}
     * <p>
     * This implementation checks whether input file exists, then validates
     * input xml against schema and if validation has been successful reads
     * descriptors collection from file
     */
    @Override
    public DescriptorCollection read() {
	DescriptorCollection result = null;

	try {
	    File file = new File(fileName);
	    if (file.exists() && validateXml(file)) {

		XMLReader reader = XMLReaderFactory.createXMLReader();
		reader.setContentHandler(parser);
		reader.parse(file.getAbsolutePath());
		/*
		 * while parsing SaxParser sends callback using
		 * collectionParsed()
		 */

		if (descriptorsList != null) {
		    result = new DescriptorCollection(descriptorsList);

		    // reset collection for next possible call of read()
		    descriptorsList = null;
		}
	    }
	} catch (Exception e) {
	    // nothing todo here
	}

	return result;
    }

    @Override
    public void collectionParsed(List<EventDescriptor> list) {
	this.descriptorsList = list;

    }

    private boolean validateXml(File f) {
	return validator.validate(f);
    }

    final XmlValidator validator = new XmlValidator();
    List<EventDescriptor> descriptorsList;
    SaxParser parser;
    String fileName;
}
